import fg from 'fast-glob'
import { isEmpty } from 'lodash-es'
import * as path from 'path'
import { posix as posixPath } from 'path'
import { ResolvedOptions, Route, UserOptions } from './types'
import { getIgnore, normalizePath, toArray } from './utils'
import escapeStringRegexp from 'escape-string-regexp'

const defaultReplaceDynamicPatterns = {
  number: '\\d+|\\d+.\\d+|0[xX][0-9a-fA-F]+',
  int: '\\d+|0[xX][0-9a-fA-F]+',
  any: '.*',
}

/**
 * Resolves the dirs for its for its given globs
 */
export function getDirs(dir: string, root: string, exclude?: string[]): string[] {
  const dirs = fg.sync(dir, {
    ignore: getIgnore(exclude),
    onlyDirectories: true,
    dot: true,
    unique: true,
    cwd: root,
  })

  return dirs
}

function resolvePageDirs(
  pagesDir: UserOptions['pagesDir'],
  root: string,
  _extensions?: string[],
  _exclude?: string[],
  _extendRoute?: (route: Route, parent?: Route) => Route | void
) {
  // : ResolvedOptions['pagesDir']
  pagesDir = toArray(pagesDir)

  return pagesDir.flatMap((pagesDir) => {
    let dir: string
    let baseRoute: string
    let extensions: any[]
    let exclude: string[]
    let extendRoute: (route: Route, parent?: Route) => Route

    if (typeof pagesDir === 'string') {
      ;(dir = pagesDir), (baseRoute = '/')
      extensions = _extensions ?? []
      exclude = _exclude!
      extendRoute = (route, parent) => {
        return _extendRoute?.(route, parent) || route
      }
    } else {
      dir = pagesDir.dir
      baseRoute = pagesDir.baseRoute ?? '/'
      extensions = pagesDir.extensions ?? _extensions ?? []
      exclude = [...(pagesDir.exclude ?? []), ...(_exclude ?? [])]
      extendRoute = (route, parent) => {
        route = pagesDir.extendRoute?.(route, parent) || route
        return _extendRoute?.(route, parent) || route
      }
    }

    const extensionsRE = new RegExp(`\\.(${extensions.map(escapeStringRegexp).join('|')})$`, 'i')
    baseRoute = normalizePath('/' + baseRoute.replace(/^\//, '').replace(/\/$/, ''))
    exclude = getIgnore(exclude)

    if (!/[\*\{\}\?\(\)]/.test(dir)) {
      dir = normalizePath(path.relative(path.resolve(root), posixPath.join(root, dir)))
      return {
        dir,
        baseRoute,
        extensionsRE,
        extensions,
        exclude,
        extendRoute,
      }
    } else {
      return getDirs(dir, root, exclude).map((dir) => {
        dir = normalizePath(dir)
        return {
          dir,
          baseRoute,
          extensionsRE,
          extensions,
          exclude,
          extendRoute,
        }
      })
    }
  })
}

export function resolveOptions(
  userOptions: UserOptions,
  viteRoot?: string,
  isbuild = false
): ResolvedOptions {
  let {
    pagesDir = ['src/pages'],
    keepExtraAttrs = false,
    enableRouteBlock = false,
    routeBlockLang = 'json5',
    exclude = [],
    syncIndex = true,
    replaceSquareBrackets = false,
    replaceDynamicPatterns,
    nuxtStyle = false,
    react = false,
    extendRoute
  } = userOptions

  const root = normalizePath(path.relative(process.cwd(), path.resolve(viteRoot ?? './')))
  const importMode = userOptions.importMode || (react ? 'sync' : 'async')

  const extensions = isEmpty(userOptions.extensions)
    ? react
      ? ['tsx', 'ts', 'jsx', 'js']
      : ['vue', 'tsx', 'ts', 'jsx', 'js']
    : userOptions.extensions!

  const resolvedPagesDir = resolvePageDirs(pagesDir, root, extensions, exclude, extendRoute)

  replaceDynamicPatterns = {
    ...defaultReplaceDynamicPatterns,
    ...replaceDynamicPatterns,
  }

  const resolvedOptions: ResolvedOptions = {
    pagesDir: resolvedPagesDir,
    keepExtraAttrs,
    enableRouteBlock,
    routeBlockLang,
    root,
    extensions,
    importMode,
    exclude,
    syncIndex,
    replaceSquareBrackets,
    replaceDynamicPatterns,
    nuxtStyle,
    react,
    isbuild,
  }

  return resolvedOptions
}
